{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Лекция 2. Пространства имён, функции и ввод-вывод"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<br />"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "##### Функции, объявление и определение (declaration / definition)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Определение (definition) функции - описание её \"интерфейса\" (сигнатуры, возвращаемого типа и квалификаторов) И реализации."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```c++\n",
    "float abs(float x)\n",
    "{\n",
    "    if (x >= 0)\n",
    "        return x;\n",
    "    return -x;\n",
    "}\n",
    "\n",
    "[[nodiscard]] float min_value(const std::vector<float>& items) noexcept\n",
    "{\n",
    "    float rv = items.front();\n",
    "    for (float x : items)\n",
    "        rv = std::min(rv, x);\n",
    "    return rv;\n",
    "}\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Замечание:\n",
    "* для поиска минимума, конечно же, используйте `std::min_element`\n",
    "* а для вычисления абсолютных величин функцию `abs` из `#include <cmath>`"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Объявление (declaration) функции - описание её \"интерфейса\""
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```c++\n",
    "float abs(float x);\n",
    "\n",
    "[[nodiscard]] float min_value(const std::vector<float>& items) noexcept;\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Если не прописано явно, у функции в программе может быть несколько объявлений, но не больше одного определения."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "__Вопрос__: что помещается в header-файл, что в cpp-файл?"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<br />"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "##### Передача аргументов в функцию и возвращаемое значение"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Передача по значению и по ссылке\n",
    "(нарисовать происходящее в памяти)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```c++\n",
    "std::vector<float> v = {1.f, 2.f, 3.f};\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Передача по значению - создание копии аргумента"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```c++\n",
    "float min_value_1(std::vector<float> items);\n",
    "\n",
    "float m = min_value_1(v);\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Передача по ссылке - работа с аргументом"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```c++\n",
    "float min_value_2(std::vector<float>& items);\n",
    "\n",
    "float m = min_value_2(v);\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Запрет на модификацию"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```c++\n",
    "float min_value_3(const std::vector<float>& items);\n",
    "\n",
    "float m = min_value_3(v);\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Что здесь происходит?"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```c++\n",
    "float f(std::vector<float>* items);\n",
    "float f(const std::vector<float>* items);\n",
    "float f(std::vector<float>* const items);\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<br />"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Возращаемое значение"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```c++\n",
    "void say_hello(const std::string& name)\n",
    "{\n",
    "    std::cout << \"hello, \" << name;\n",
    "}\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```c++\n",
    "std::vector<std::string> make_team()\n",
    "{\n",
    "    return { \"Bifur\", \"Bofur\", \"Bombur\", \"Oin\",\n",
    "             \"Gloin\", \"Fili\", \"Nori\", \"Dori\",\n",
    "             \"Dwalin\", \"Ori\", \"Balin\", \"Kili\" };\n",
    "}\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```c++\n",
    "bool append_teamlead(std::vector<std::string>& team)\n",
    "{\n",
    "    if (in_Shire())\n",
    "    {\n",
    "        team.push_back(\"Thorin\");\n",
    "        return true;\n",
    "    }\n",
    "    return false;\n",
    "}\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Немного примеров посложнее"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```c++\n",
    "std::string* find_balin(const std::vector<std::string>& team)\n",
    "{\n",
    "    for (const auto& member : team)\n",
    "        if (member == \"Balin\")\n",
    "            return &member;\n",
    "    return nullptr;\n",
    "}\n",
    "\n",
    "// usage 1\n",
    "std::vector<std::string> team = make_team();\n",
    "if (std::string* maybe_balin = find_balin(team))\n",
    "    std::cout << *maybe_balin;\n",
    "\n",
    "// usage 2\n",
    "if (std::string* maybe_balin = find_balin({\"Ori\", \"Gloin\", \"Balin\"}))\n",
    "    std::cout << *maybe_balin;  // OOOPS\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Ещё пример:"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```c++\n",
    "const std::string& get_value_from_cache(Cache c,\n",
    "                                        int key,\n",
    "                                        const std::string& default_value)\n",
    "{\n",
    "    if (c.contains(key))\n",
    "        return c[key];\n",
    "    return default_value;\n",
    "}\n",
    "// TODO: в чём проблема этой функции?\n",
    "//           (а) конкретно про производительность\n",
    "//           (б) их как минимум две\n",
    "\n",
    "// usage 1\n",
    "const std::string default_value = \"Bilbo\";\n",
    "std::cout << get_value_from_cache(cache, missing_key, default_value);  // OK\n",
    "        \n",
    "// usage 2\n",
    "std::cout << get_value_from_cache(cache, missing_key, \"Bilbo\");  // OK\n",
    "        \n",
    "// usage 3\n",
    "std::string value = get_value_from_cache(cache, missing_key, \"Bilbo\");  // OK\n",
    "std::cout << value;\n",
    "        \n",
    "// usage 4\n",
    "const std::string& value = get_value_from_cache(cache, missing_key, \"Bilbo\");\n",
    "std::cout << value;  // OOOOPS\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "__Вопрос__: что будет напечатано программой ниже?\n",
    "\n",
    "Показать пример на godbolt.org на clang 8.0.0 с разными оптимизациями, чтобы наглядно продемонстрировать ub"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```c++\n",
    "#include <iostream>\n",
    "#include <string>\n",
    "\n",
    "const std::string& f(const bool x,\n",
    "                     const std::string& default_value1,\n",
    "                     const std::string& default_value2)\n",
    "{\n",
    "    return x ? default_value1 : default_value2;\n",
    "}\n",
    "\n",
    "int main()\n",
    "{\n",
    "    std::cout << \"hello world\" << std::endl;\n",
    "\n",
    "    const std::string& s = f(true, \"123\", \"12345\");\n",
    "    std::cout << s << std::endl;\n",
    "    return 0;\n",
    "}\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<br />"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "##### Порядок вычисления аргументов"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "__Вопрос__: что будет распечатано в примерах?"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Пример 1:"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```c++\n",
    "int a() { std::cout << 'a'; return 1; }\n",
    "int b() { std::cout << 'b'; return 2; }\n",
    "int c() { std::cout << 'c'; return 3; }\n",
    "\n",
    "int sum(int a, int b int c) {\n",
    "    return a + b + c;\n",
    "}\n",
    "\n",
    "int main() {\n",
    "    std::cout << sum(a(), b(), c()) << std::endl;\n",
    "    return 0;\n",
    "}\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Пример 2:"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```c++\n",
    "int a() { std::cout << 'a'; return 1; }\n",
    "int b() { std::cout << 'b'; return 2; }\n",
    "int c() { std::cout << 'c'; return 3; }\n",
    "\n",
    "int main() {\n",
    "    std::cout << a() + b() * c() << std::endl;\n",
    "    return 0;\n",
    "}\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Ответ:"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "__Порядок вычисления аргументов не определён!__\n",
    "\n",
    "https://en.cppreference.com/w/cpp/language/eval_order"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<br />"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "##### Значения аргументов по умолчанию"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Можно задавать значения аргументов по умолчанию:"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```c++\n",
    "std::string convert_to_string(int value, int base = 10);\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```c++\n",
    "std::string join_strings(const std::vector<std::string>& strings,\n",
    "                         const std::string& sep = \"\\n\");\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "использование:"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```c++\n",
    "std::string s1 = convert_to_string(42);    // 42\n",
    "std::string s2 = convert_to_string(42, 2); // 101010\n",
    "\n",
    "std::string song = join_strings({\"In the town where I was born\",\n",
    "                                 \"Lived a man who sailed to sea\",\n",
    "                                 \"And he told us of his life\",\n",
    "                                 \"In the land of submarines\"});\n",
    "std::string sentence = join_strings({\"Run\", \"Forest\", \"run\"}, \" \");\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Но такие аргументы должны быть последними:"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```c++\n",
    "std::string join_strings(const std::vector<std::string>& strings,\n",
    "                         const std::string& sep = \"\\n\",\n",
    "                         bool skip_empty_lines);  // compilation ERROR!\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```c++\n",
    "std::string join_strings(const std::vector<std::string>& strings,\n",
    "                         const std::string& sep = \"\\n\",\n",
    "                         bool skip_empty_lines = false);  // OK\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<br />"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "##### Перегрузка функции"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "https://en.cppreference.com/book/intro/function_overloading"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Задача - реализовать конвертацию всего в строку. Желательно единообразно и чтобы: есть способ - компилируется и работает, нет способа - не компилируется."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```c++\n",
    "std::string convert_to_string(int x);       // 1\n",
    "std::string convert_to_string(unsigned x);  // 2\n",
    "std::string convert_to_string(float x);     // 3\n",
    "\n",
    "std::cout << convert_to_string(5);    // 1\n",
    "std::cout << convert_to_string(5u);   // 2\n",
    "std::cout << convert_to_string(5.f);  // 3\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<br />"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "##### Пространства имён и name mangling при компиляции"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "https://en.wikipedia.org/wiki/Name_mangling"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Пространства времён решают проблему, когда функция с одинаковым именем имеет две реализации в разных билиотеках"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```c++\n",
    "// library for json parsing\n",
    "\n",
    "// return version of json parsing library\n",
    "std::string get_version();\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```c++\n",
    "// library for image manipulation\n",
    "\n",
    "// return version of image manipulation library\n",
    "std::string get_version();\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Такой код скомпилируется, но не слинкуется, т.к. для функции `get_version` нарушено ODR-правило (One Definition Rule)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```c++\n",
    "// library for json parsing\n",
    "\n",
    "namespace cool_json_parser\n",
    "{\n",
    "    // return version of json parsing library\n",
    "    std::string get_version();\n",
    "}\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```c++\n",
    "// library for image manipulation\n",
    "\n",
    "namespace cool_image_processing\n",
    "{\n",
    "   // return version of image manipulation library\n",
    "    std::string get_version(); \n",
    "}\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Такой код слинкуется, т.к. работает механизм name mangling во время компиляции"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Показать пример на godbolt.org, не забыть убрать галочку demangle"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```c++\n",
    "#include <iostream>\n",
    "#include <string>\n",
    "\n",
    "std::string get_version(int x, int y, long z);\n",
    "\n",
    "namespace cool_json_parser\n",
    "{\n",
    "    std::string get_version(int x, int y, long z);\n",
    "}\n",
    "\n",
    "namespace cool_image_processing\n",
    "{\n",
    "    std::string get_version(int x, int y, long z);\n",
    "}\n",
    "\n",
    "int main()\n",
    "{\n",
    "    std::cout << get_version(0, 0, 0);\n",
    "    std::cout << cool_json_parser::get_version(0, 0, 0);\n",
    "    std::cout << cool_image_processing::get_version(0, 0, 0);\n",
    "    return 0;\n",
    "}\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "В результате name mangling компилятор clang создаёт символы:\n",
    "* `_Z11get_versionB5cxx11iil`\n",
    "* `_ZN16cool_json_parser11get_versionB5cxx11Eiil`\n",
    "* `_ZN21cool_image_processing11get_versionB5cxx11Eiil`\n",
    "\n",
    "Какой из них к чему относится, догадаться не сложно. "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<br />"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "##### ADL (Argument Dependent Lookup)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "https://en.cppreference.com/w/cpp/language/adl"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Теперь мы реализовали свой класс `Point` в нашем `namespace math` и хотим, чтобы его объекты тоже можно было конвертировать в строку. Желательно единообразно."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```c++\n",
    "namespace mymath\n",
    "{\n",
    "    struct Point\n",
    "    {\n",
    "        float x;\n",
    "        float y;\n",
    "    }\n",
    "    \n",
    "    std::string convert_to_string(Point p)\n",
    "    {\n",
    "        return convert_to_string(p.x) + \" \" + convert_to_string(p.y);\n",
    "    }\n",
    "}\n",
    "\n",
    "int main()\n",
    "{\n",
    "    mymath::Point p{3.f, 4.f};\n",
    "    \n",
    "    // явно не указано пространство имён mymath у функции\n",
    "    // convert_to_string, но ADL нужную функцию найдёт \n",
    "    std::cout << convert_to_string(p) << std::endl;\n",
    "    \n",
    "    return 0;\n",
    "}\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "ADL - набор правил, который добавляет к области поиска функции пространства имён, содержащих её аргументы"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<br />"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "##### Функции (операторы) ввода-вывода"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Операторы - те же функции, но в альтернативной записи:"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```c++\n",
    "namespace std\n",
    "{\n",
    "    // есть в стандартной библиотеке\n",
    "    string operator + (const string& l, const string& r);\n",
    "}\n",
    "\n",
    "namespace mymath\n",
    "{\n",
    "    // личный оператор сложения для точек\n",
    "    Point operator + (const Point& l, const Point& r);\n",
    "}\n",
    "\n",
    "int main()\n",
    "{\n",
    "    std::string s1 = \"s1\", s2 = \"s2\";\n",
    "    mymath::Point p1{0, 1}, p2{1, 0};\n",
    "    \n",
    "    std::string s = s1 + s2;\n",
    "    mymath::Point p = p1 + p2;\n",
    "    \n",
    "    return 0;\n",
    "}\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Можно доопределить свои операторы для стандартных типов"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```c++\n",
    "namespace std\n",
    "{\n",
    "    string operator * (const string& l, unsigned count)\n",
    "    {\n",
    "        string rv;\n",
    "        for (unsigned i = 0; i < count; ++i)\n",
    "            rv += l;\n",
    "        return rv;\n",
    "    }\n",
    "}\n",
    "\n",
    "std::cout << std::string(\"nom \") * 3;  // \"nom nom nom \"\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "И их использование строится на тех же правилах: перегрузка + ADL"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Пример для операторов вывода:"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```c++\n",
    "// from standard lib\n",
    "namespace std\n",
    "{\n",
    "    std::ostream& operator << (std::ostream& os, int x);\n",
    "    std::ostream& operator << (std::ostream& os, float x);\n",
    "    std::ostream& operator << (std::ostream& os, const char* x);\n",
    "    std::ostream& operator << (std::ostream& os, const std::string& x);\n",
    "}\n",
    "\n",
    "// your own\n",
    "namespace mymath\n",
    "{\n",
    "    std::ostream& operator << (std::ostream& os, Point p)\n",
    "    {\n",
    "        return os << p.x << \" \" << p.y;\n",
    "    }\n",
    "}\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<br />"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "__Замечания по лекции:__\n",
    "* Про namespace - дорассказать пораньше. ADL, `nodiscard`, ввод-вывод вынести либо на конец курса либо во вторую часть курса.\n",
    "* Чтобы понимали лучше рассказ про референсы на протухшие объекты, нужно рисовать происходящее в стеке и куче. Возможно, пример с `std::initializer_list` заменить на передачу `make_team` аргументом. Или сначала передачу `make_team`, а потом `std::initializer_list` для закрепления.\n",
    "* Референсы на протухшие объекты - обязательно в повторение на следующую лекцию.\n",
    "* Ещё раз повторить про указатели, ссылки и значения. Попробовать найти другое объяснение."
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.9"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
